'use strict';

class Galaxy {

    constructor(node, galaxy) {
        this.w = galaxy.width = node.width();
        this.h = galaxy.height = node.height();
        this.ctx = galaxy.getContext('2d');
        this.opts = {
            lineCount: 150,
            starCount: 150,
            radVel: .01,
            lineBaseVel: .1,
            lineAddedVel: .1,
            lineBaseLife: .4,
            lineAddedLife: .01,
            starBaseLife: 10,
            starAddedLife: 10,
            ellipseTilt: -.3,
            ellipseBaseRadius: .15,
            ellipseAddedRadius: .02,
            ellipseAxisMultiplierX: 2.5,
            ellipseAxisMultiplierY: 1,
            ellipseCX: this.w / 2,
            ellipseCY: this.h / 2,
            repaintAlpha: .015
        };
        this.lines = [];
        this.stars = [];
        this.tick = 0;
        this.parent = node;
        window.addEventListener('resize', () => {
            this.w = galaxy.width = this.parent.width();
            this.h = galaxy.height = this.parent.height();
            this.opts.ellipseCX = this.w / 2;
            this.opts.ellipseCY = this.h / 2;
            this.init();
        });
        this.init();
    }

    init() {
        this.lines.length = this.stars.length = 0;
        this.ctx.globalCompositeOperation = 'source-over';
        this.ctx.fillStyle = '#333';
        this.ctx.fillRect(0, 0, this.w, this.h);
        this.loop();
    }

    loop() {
        window.requestAnimationFrame(() => this.loop());
        this.step();
        this.draw();
    }

    step() {
        this.tick += .5;
        if (this.lines.length < this.opts.lineCount && Math.random() < .5)
            this.lines.push(new Line(this));
        if (this.stars.length < this.opts.starCount)
            this.stars.push(new Star(this));
        this.lines.map(function (line) {
            line.step();
        });
        this.stars.map(function (star) {
            star.step();
        });
    }

    draw() {
        this.ctx.shadowBlur = 0;
        this.ctx.globalCompositeOperation = 'source-over';
        this.ctx.fillStyle = 'rgba(0,0,0,alp)'.replace('alp', this.opts.repaintAlpha);
        this.ctx.fillRect(0, 0, this.w, this.h);
        this.ctx.globalCompositeOperation = 'lighter';
        this.ctx.translate(this.opts.ellipseCX, this.opts.ellipseCY);
        this.ctx.rotate(this.opts.ellipseTilt);
        this.ctx.scale(this.opts.ellipseAxisMultiplierX, this.opts.ellipseAxisMultiplierY);
        this.lines.map(function (line) {
            line.draw();
        });
        this.ctx.scale(1 / this.opts.ellipseAxisMultiplierX, 1 / this.opts.ellipseAxisMultiplierY);
        this.ctx.rotate(-this.opts.ellipseTilt);
        this.ctx.translate(-this.opts.ellipseCX, -this.opts.ellipseCY);
        this.stars.map(function (star) {
            star.draw();
        });
    }
}

class Line {
    constructor(galaxy) {
        this.galaxy = galaxy;
        this.reset();
    }

    reset() {
        this.rad = Math.random() * Math.PI * 2;
        this.len = this.galaxy.w * (this.galaxy.opts.ellipseBaseRadius + Math.random() * this.galaxy.opts.ellipseAddedRadius);
        this.lenVel = this.galaxy.opts.lineBaseVel + Math.random() * this.galaxy.opts.lineAddedVel;
        this.x = this.px = Math.cos(this.rad) * this.len;
        this.y = this.py = Math.sin(this.rad) * this.len;
        this.life = this.originalLife = this.galaxy.w * (this.galaxy.opts.lineBaseLife + Math.random() * this.galaxy.opts.lineAddedLife);
        this.alpha = .2 + Math.random() * .8;
    }

    step() {
        --this.life;
        var ratio = 1 - .1 * this.life / this.originalLife;
        this.px = this.x;
        this.py = this.y;
        this.rad += this.galaxy.opts.radVel;
        this.len -= this.lenVel;
        this.x = Math.cos(this.rad) * this.len;
        this.y = Math.sin(this.rad) * this.len;
        if (this.life <= 0)
            this.reset();
    }

    draw() {
        var ratio = Math.abs(this.life / this.originalLife - 1 / 2);
        this.galaxy.ctx.lineWidth = ratio * 5;
        this.galaxy.ctx.strokeStyle = this.galaxy.ctx.shadowColor = 'hsla(hue, 80%, light%, alp)'
            .replace('hue', this.galaxy.tick + this.x / (this.galaxy.w * (this.galaxy.opts.ellipseBaseRadius + this.galaxy.opts.ellipseAddedRadius)) * 100)
            .replace('light', 75 - ratio * 150)
            .replace('alp', this.alpha);
        this.galaxy.ctx.beginPath();
        this.galaxy.ctx.moveTo(this.px, this.py);
        this.galaxy.ctx.lineTo(this.x, this.y);
        this.galaxy.ctx.stroke();
    }
}

class Star {

    constructor(galaxy) {
        this.galaxy = galaxy;
        this.reset();
    }

    reset() {
        this.x = Math.random() * this.galaxy.w;
        this.y = Math.random() * this.galaxy.h;
        this.life = this.galaxy.opts.starBaseLife + Math.random() * this.galaxy.opts.starAddedLife;
    }

    step() {
        --this.life;
        if (this.life <= 0)
            this.reset();
    }

    draw() {
        this.galaxy.ctx.fillStyle = this.galaxy.ctx.shadowColor = 'hsla(hue, 80%, 50%, .2)'.replace('hue', this.galaxy.tick + this.x / this.galaxy.w * 100);
        this.galaxy.ctx.shadowBlur = this.life;
        this.galaxy.ctx.fillRect(this.x, this.y, 1, 1);
    }
}